﻿using BullsAndCowsGameUT;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using Wintellect.PowerCollections;
using System.IO;

namespace BullsAndCowsUnitTests
{


    /// <summary>
    ///This is a test class for BullsAndCowsGameTest and is intended
    ///to contain all BullsAndCowsGameTest Unit Tests
    ///</summary>
    [TestClass()]
    public class BullsAndCowsGameTest
    {
        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        // 
        //You can use the following additional attributes as you write your tests:
        //
        //Use ClassInitialize to run code before running the first test in the class
        //[ClassInitialize()]
        //public static void MyClassInitialize(TestContext testContext)
        //{
        //}
        //
        //Use ClassCleanup to run code after all tests in a class have run
        //[ClassCleanup()]
        //public static void MyClassCleanup()
        //{
        //}
        //
        //Use TestInitialize to run code before running each test
        //[TestInitialize()]
        //public void MyTestInitialize()
        //{
        //}
        //
        //Use TestCleanup to run code after each test has run
        //[TestCleanup()]
        //public void MyTestCleanup()
        //{
        //}
        //
        #endregion


        /// <summary>
        ///Tests for InputIsValid
        ///</summary>

        [TestMethod()]
        public void InputIsValidTest()
        {
            BullsAndCowsGame game = new BullsAndCowsGame();
            game.Input = "1234";
            bool valid = game.InputIsValid();
            Assert.AreEqual(valid, true, "Failed input test");
        }

        [TestMethod()]
        public void InputIsValidValidCommandTest()
        {
            BullsAndCowsGame game = new BullsAndCowsGame();
            game.Input = "exit";
            bool validExit = game.InputIsValid();
            game.Input = "restart";
            bool validRestart = game.InputIsValid();
            game.Input = "top";
            bool validTop = game.InputIsValid();
            game.Input = "help";
            bool validHelp = game.InputIsValid();
            Assert.AreEqual(validExit, true, "Failed on exit command");
            Assert.AreEqual(validRestart, true, "Failed on restart command");
            Assert.AreEqual(validTop, true, "Failed on top command");
            Assert.AreEqual(validHelp, true, "Failed on help command");
        }

        [TestMethod()]
        [ExpectedException(typeof(ArgumentException))]
        public void InputIsValidExeptionTest()
        {
            BullsAndCowsGame game = new BullsAndCowsGame();
            game.Input = "12as";
            game.InputIsValid();
        }

        /// <summary>
        ///A test for GenerateSecretNumber
        ///</summary>
        [TestMethod()]
        public void GenerateSecretNumberTest()
        {
            BullsAndCowsGame game = new BullsAndCowsGame();
            string number = game.GenerateSecretNumber();
            Assert.AreEqual(number.Length, 4, "Generated number with wrong length");
            int num = -1;
            int.TryParse(number, out num);
            Assert.AreNotEqual(num, -1, "Generated NaN");
        }

        /// <summary>
        ///A test for BullsAndCowsGame Constructor
        ///</summary>
        [TestMethod()]
        public void BullsAndCowsGameConstructorTest()
        {
            BullsAndCowsGame game = new BullsAndCowsGame();
            int expectedBulls = 0;
            int expectedCows = 0;
            int expectedAttempts = 0;
            int expectedCheats = 0;
            string expectedName = "";
            int[] expectedHelpMask = new int[] { 0, 0, 0, 0 };
            string expectedInput = "Game on";
            bool equalHelpMasks = false;
            for (int i = 0; i < 4; i++)
            {
                if (expectedHelpMask[i] == game.HelpMask[i])
                {
                    equalHelpMasks = true;
                }
            }

            Assert.AreEqual(expectedBulls, game.Bulls, "Wrong initial value for bulls");
            Assert.AreEqual(expectedCows, game.Cows, "Wrong initial value for cows");
            Assert.AreEqual(expectedAttempts, game.Attempts, "Wrong initial value for attempts");
            Assert.AreEqual(expectedCheats, game.Cheats, "Wrong initial value for cheats");
            Assert.AreEqual(expectedName, game.Name, "Wrong initial value for name");
            Assert.AreEqual(equalHelpMasks, true, "Wrong initial value for helpMask");
            Assert.AreEqual(expectedInput, game.Input, "Wrong initial value for input");
        }

        /// <summary>
        ///Tests for AddToScoreboard
        ///</summary>
        [TestMethod()]
        public void AddToScoreboardTest()
        {
            BullsAndCowsGame game = new BullsAndCowsGame();
            game.Attempts = 7;
            game.AddToScoreboard("Ivan");
            game.Attempts = 5;
            game.AddToScoreboard("Kiro");
            game.AddToScoreboard("Petko");
            int count = game.Scoreboard.Values.Count;
            int expectedCount = 3;
            Assert.AreEqual(expectedCount, count, "Wrong scoreboard count");
        }

        [TestMethod()]
        public void AddToScoreboardAddMoreThanFiveTest()
        {
            BullsAndCowsGame game = new BullsAndCowsGame();

            for (int i = 0; i < 3; i++)
            {
                game.Attempts = i + 3;
                game.AddToScoreboard("Ivan" + i.ToString());
            }
            for (int i = 0; i < 5; i++)
            {
                game.AddToScoreboard("Desi" + i.ToString());
            }
            int expectedCount = 5;
            int count = game.Scoreboard.Values.Count;
            Assert.AreEqual(expectedCount, count, "Wrong scoreboard count");
        }

        /// <summary>
        ///A test for FindBullsCows
        ///</summary>
        [TestMethod()]
        public void FindBullsCowsTest()
        {
            BullsAndCowsGame game = new BullsAndCowsGame();
            int expectedBulls = 2;
            int expectedCows = 2;
            game.SecretNumber = "1234";
            game.Input = "4231";
            game.FindBullsCows();
            Assert.AreEqual(expectedBulls, game.Bulls, "Wrong number of bulls found");
            Assert.AreEqual(expectedCows, game.Cows, "Wrong number of cows found");
        }

        /// <summary>
        ///Tests for Display
        ///</summary>
        [TestMethod()]
        public void DisplayNormalCaseTest()
        {
            StringWriter consoleRedirect = new StringWriter();
            Console.SetOut(consoleRedirect);
            BullsAndCowsGame game = new BullsAndCowsGame();
            game.Bulls = 3;
            game.Cows = 1;
            game.Display();
            string expectedDisplay = string.Format("Wrong number! Bulls: {0}, Cows: {1}\nEnter your guess or command: ", game.Bulls, game.Cows);
            string display = consoleRedirect.ToString();
            Assert.AreEqual(expectedDisplay, display, "Wrong output");
        }

        [TestMethod()]
        public void DisplayCheaterTest()
        {
            StringWriter consoleRedirect = new StringWriter();
            Console.SetOut(consoleRedirect);
            BullsAndCowsGame game = new BullsAndCowsGame();
            game.Bulls = 4;
            game.Cows = 0;
            game.Cheats = 2;
            game.Attempts = 4;
            game.Display();
            string expectedDisplay = string.Format(string.Format("Congratulations! You guessed the secret number in {0} attempts and {1} cheats." +
                        "\nYou are not allowed to enter the top scoreboard.", game.Attempts, game.Cheats));
            string display = consoleRedirect.ToString();
            Assert.AreEqual(expectedDisplay, display, "Wrong output");
        }

        [TestMethod()]
        public void DisplayFirstWinnerTest()
        {
            StringWriter consoleRedirect = new StringWriter();
            Console.SetOut(consoleRedirect);
            BullsAndCowsGame game = new BullsAndCowsGame();
            game.Bulls = 4;
            game.Cows = 0;
            game.Cheats = 0;
            game.Attempts = 4;
            game.Display();
            string expectedDisplay = string.Format("Congratulations! You guessed the secret number in {0} attempts." +
            "\nPlease enter your name for the top scoreboard: ", game.Attempts);
            string display = consoleRedirect.ToString();
            Assert.AreEqual(expectedDisplay, display, "Wrong output");
            int count = game.Scoreboard.Values.Count;
            Assert.AreEqual(count, 1, "Winner not added");
        }

        [TestMethod()]
        public void DisplayWithMoreThanFiveWinnersTest()
        {
            StringWriter consoleRedirect = new StringWriter();
            Console.SetOut(consoleRedirect);
            BullsAndCowsGame game = new BullsAndCowsGame();
            game.Bulls = 4;
            game.Cows = 0;
            for (int i = 0; i < 6; i++)
            {
                game.Attempts++;
                game.AddToScoreboard("Ivan" + i);
            }
            game.Attempts = 2;
            game.Display();
            string expectedDisplay = string.Format("Congratulations! You guessed the secret number in {0} attempts." +
                "\nPlease enter your name for the top scoreboard: ", game.Attempts);
            string display = consoleRedirect.ToString();
            Assert.AreEqual(expectedDisplay, display, "Wrong output");
        }

        /// <summary>
        ///Tests for DisplayHelp
        ///</summary>
        [TestMethod()]
        public void DisplayHelpTest()
        {
            StringWriter consoleRedirect = new StringWriter();
            Console.SetOut(consoleRedirect);
            BullsAndCowsGame game = new BullsAndCowsGame();
            game.Cheats = 0;
            game.DisplayHelp();
            string display = consoleRedirect.ToString();
            int xCount = 0;
            int digitsCount = 0;
            foreach (char ch in display)
            {
                if (ch == 'X')
                {
                    xCount++;
                }
                if (char.IsDigit(ch))
                {
                    digitsCount++;
                }
            }
            Assert.AreEqual(xCount + digitsCount, 4, "Wrong number of digits displayed in help");
        }

        [TestMethod()]
        public void DisplayBlindHelpTest()
        {
            StringWriter consoleRedirect = new StringWriter();
            Console.SetOut(consoleRedirect);
            BullsAndCowsGame game = new BullsAndCowsGame();
            game.Cheats = 4;
            game.DisplayHelp();
            string expectedDisplay = "Are you blind?\r\n";
            string display = consoleRedirect.ToString();

            Assert.AreEqual(expectedDisplay, display, "Help shown without need");
        }

        /// <summary>
        ///A test for DisplayTop
        ///</summary>
        [TestMethod()]
        public void DisplayTopTest()
        {
            StringWriter consoleRedirect = new StringWriter();
            Console.SetOut(consoleRedirect);
            BullsAndCowsGame game = new BullsAndCowsGame();
            game.Attempts = 7;
            game.AddToScoreboard("Ivan");
            game.DisplayTop();
            string expectedDisplay = "1. Ivan --> 7 attempts\r\nEnter your guess or command: ";
            string display = consoleRedirect.ToString();
            Assert.AreEqual(expectedDisplay, display, "Wrong date printed in top scoreboard");
        }

        /// <summary>
        ///A test for SayHello
        ///</summary>
        [TestMethod()]
        public void SayHelloTest()
        {
            StringWriter consoleRedirect = new StringWriter();
            Console.SetOut(consoleRedirect);
            BullsAndCowsGame game = new BullsAndCowsGame();
            game.SayHello();
            string expectedDisplay = "Welcome to “Bulls and Cows” game. Please try to guess my secret 4-digit number.\r\n" +
                "Use 'top' to view the top scoreboard, 'restart' to start a new game and 'help'\n" +
                "to cheat and 'exit' to quit the game.\nEnter your guess or command: ";
            string display = consoleRedirect.ToString();
            Assert.AreEqual(expectedDisplay, display, "Wrong welcome message printed");
        }

        /// <summary>
        ///Tests for Winner
        ///</summary>
        [TestMethod()]
        public void WinnerTest()
        {
            BullsAndCowsGame game = new BullsAndCowsGame();
            game.Bulls = 4;
            bool expected = true;
            bool actual = game.Winner();
            Assert.AreEqual(expected, actual, "Game not won with all digits guessed");
        }

        [TestMethod()]
        public void WinnerPrematureTest()
        {
            BullsAndCowsGame game = new BullsAndCowsGame();
            game.Bulls = 2;
            bool expected = false;
            bool actual = game.Winner();
            Assert.AreEqual(expected, actual, "Game won without all digits guessed");
        }
    }
}